﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web.Script.Serialization;
using Microsoft.Win32;
using Debug = System.Diagnostics.Debug;
namespace Piao
{
    public delegate string InputCaptchaDelegate(Image img);

    class UserOpration
    {
        [DllImport("winmm")]
        private static extern bool PlaySound(string szSound, IntPtr hMod, int i);
        public static bool CancelState { get; set; }

        protected UserOpration()
        { }

        private static UserOpration instance;
        private WebUtil web = new WebUtil();

        public string ProxyName
        {
            set
            {
                if (!string.IsNullOrEmpty(value))
                {
                    web.Proxy = new System.Net.WebProxy(value);
                    Log.Message("change proxy to [{0}]", value.ToUpper());
                }
            }
        }

        /// <summary>
        ///注意：除非另外声明，否则每个 DWORD 值等于 0、1 或 3。
        ///通常，设置为 0 则将具体操作设置为允许；设置为 1 则导致出现提示；设置为 3 则禁止执行具体操作。
        /// </summary>
        public int IESetting
        {
            // http://support.microsoft.com/kb/182569/zh-cn

            get
            {
                RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3", true);
                string val = key.GetValue("1609", "3").ToString();
                return int.Parse(val);
            }
            set
            {
                RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3", true);
                key.SetValue("1609", value, RegistryValueKind.DWord);
            }
        }

        public static UserOpration Instance
        {
            get
            {
                if (null == instance)
                {
                    Log.Debug("instance created.");
                    instance = new UserOpration();
                    instance.IESetting = 0;
                }


                return instance;
            }
        }

        /// <summary>
        /// use a new instance when try to log in again.
        /// </summary>
        /// <returns></returns>
        public static UserOpration NewInstance()
        {
            instance = new UserOpration();
            return instance;
        }

        public void InitPreviousCookie()
        {
            web.GetCookiesForBrowser(URL_HOME);
        }

        public string ReLogin()
        {
            string response = web.Get(URL_HOME_MY);
            string username = IsAlreadyLogin(response);

            return username;
        }

        public string Login(Func<Image, string> inputCaptchaThread, string user, string password)
        {
            string webprompt = string.Empty;

            #region step1 init
            web.Get(URL_LOGIN_PAGE);
            #endregion

            #region step2 verification code image
            Random rnd = new Random(System.DateTime.Now.Millisecond);
            var image = web.DownloadCaptcha(URL_lOGIN_CAPTCHA);


            string ccc = inputCaptchaThread(image);



            if (string.IsNullOrEmpty(ccc))
            {
                Log.Error("验证码输入超时或没有有效的验证码。");
                return string.Empty;
            }

            Log.Message("登陆网页中，账号{0}, 验证码 {1}", user, ccc);
            #endregion



            Func<string> LoginPost = () =>
                {
                    RequestStringBuilder rsb = new RequestStringBuilder();

                    rsb["loginUser.user_name"] = user;
                    rsb["user.password"] = password;
                    rsb["randCode"] = ccc;
                    rsb["refundLogin"] = "N";
                    rsb["refundFlag"] = "Y";

                    #region step3 get server ram code
                    string preServerCode = web.Post(URL_POST_LOGIN_PRE, "");
                    foreach (var pair in this.ReadJson(preServerCode))
                    {
                        rsb[pair.Key] = pair.Value;
                    }
                    #endregion

                    #region step 4 login
                    string postData = rsb.ToString();
                    string postResponse = web.Post(URL_POST_LOGIN, postData);
                    #endregion

                    webprompt = IsAlreadyLogin(postResponse);
                    if (!string.IsNullOrEmpty(webprompt))
                    {
                        return webprompt;
                    }

                    string popup = GetPopupMessage(postResponse);
                    try
                    {
                        Match match = Regex.Match(postResponse, @"(系统维护中.*?(?=</))|(输入正确的验证码)|(登录名不存在)");

                        var errorSpanMatch = Regex.Match(postResponse, @"<span id=""randErr"".*?</span>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
                        var errString = Regex.Replace(errorSpanMatch.Value, "<.*?>", string.Empty, RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);

                        if (match.Success)  //系统维护
                        {
                            errString = match.Value;
                        }
                        else if (!string.IsNullOrEmpty(popup) && popup.Contains("忙") && popup.Contains("过多")) //网路忙 
                        {
                            errString = popup;
                        }
                        else if (errString.Contains("忙") || errString.Contains("再试"))
                        {
                            //do nothing, usuall will not go to here
                        }
                        else
                        {
                            //invalid password or captcha
                        }

                        if (!string.IsNullOrEmpty(errString) || !string.IsNullOrEmpty(popup))
                        {
                            Log.Error("登陆不成功. {0} {1}", errString, popup);
                            return errString;
                        }
                        webprompt = IsAlreadyLogin(postResponse);
                        if (!string.IsNullOrEmpty(webprompt))
                        {
                            Log.Message(webprompt);
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex.ToString());
                    }
                    return string.Empty;
                };


#if DEBUG
            int maxretries = 20;
#else
            int maxretries = 200;
#endif
            for (int i = 0; i < maxretries; i++)
            {
                CheckThreadStateToAbort();
                Log.Message("尝试第 {0} 次登陆。", i + 1);

                string err = LoginPost();
                if (!string.IsNullOrEmpty(err))
                    break;
                if (!string.IsNullOrEmpty(webprompt))
                    break;

                Thread.Sleep(1000);
            }

            Log.Message("网页登陆已经停止， 点击登陆重新开始。");
            return webprompt;

        }

        /// <summary>
        /// from home page to read seat types
        /// </summary>
        public void QuerySeatTypes()
        {
            string response = web.Get(URL_HOME_SEATTYPES);
            this.ReadTicketInfo(response);

            Log.Message("获取车票类型{0}个。 ", Global.TicketCodeList.Count);
        }


        public void QueryStationName()
        {
            string str = web.Get(@URL_STATION_NAMES_JS);
            var matches = Regex.Matches(str, @"\d{0,3}@\D{3}\|.*?\|[a-zA-Z]{3}", RegexOptions.IgnoreCase | RegexOptions.Compiled);

            Log.Message("获取到车站名称{0}个。", matches.Count);

            foreach (Match m in matches)
            {
                string[] arr = m.Value.Split('|');
                string key = arr[2]; //key is code, arr[1] is chinese name text
                if (!Global.StationNameDict.ContainsKey(key))
                {
                    Global.StationNameDict.Add(key, arr[1]);

                    string pj = ChinesePinYin.PinYinInitialLetter.Read(arr[1]);
                    var row = Global.StationNameTable.NewRow();
                    Global.StationNameTable.Rows.Add(row);
                    row[Global.DataComlumnChineseName] = arr[1];
                    row[Global.DataComlumnCodeName] = key;
                    row[Global.DataComlumnPinYin] = pj;
                    row[Global.DataComlumnDisplayText] = key.Substring(0, 1) + "," + arr[1];

                }
            }
        }


        public List<JsonObject> QueryStStrainAll(string date, string fromCode, string toCode)
        {
            if (fromCode == toCode) return new List<JsonObject>();


            RequestStringBuilder rsb = new RequestStringBuilder();
            rsb["date"] = date;
            rsb["fromstation"] = fromCode;
            rsb["tostation"] = toCode;
            rsb["starttime"] = "00%3A00--24%3A00";

            string postData = rsb.BuildString();

            string response = web.Post(URL_POST_QUERY_AVAILABLE_TRAINS, postData);



            if (string.IsNullOrEmpty(response)
                || response.Contains("系统忙")
                || response.Contains("系统维护中")
                || response.Contains("当前访问用户过多")
                ) return new List<JsonObject>();


            var validTrainData = Regex.Matches(response, "{.*?}");
            foreach (Match tmp in validTrainData)
            {
                var d = UserOpration.Instance.ReadJson(tmp.Value);
            }

            using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(response)))
            {
                try
                {
                    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<JsonObject>));
                    List<JsonObject> list = (List<JsonObject>)serializer.ReadObject(ms);

                    foreach (JsonObject t in list)
                    {
                        if (!Global.TrainCodeList.Contains(t))
                        {
                            Global.TrainCodeList.Add(t);
                        }
                    }

                    Log.Message("从 {0} 到 {1} 有 {2} 个车次.", Global.StationNameDict[fromCode], Global.StationNameDict[toCode], list.Count);
                    return list;
                }
                catch (Exception ex)
                {
                    Log.Error("序列化错误. {0}", ex.Message);
                }
                return new List<JsonObject>();
            }
        }


        public List<OrderData> QueryLeftTicket(string date, string fromCode, string toCode, string fromTelName, string toTelName)
        {

            if (Global.StationNameDict.Count == 0)
                QueryStationName();
            List<OrderData> trainInfoList = new List<OrderData>();


            RequestStringBuilder rsb = new RequestStringBuilder();
            Random rd = new Random(DateTime.Now.Millisecond);

            rsb["orderRequest.train_date"] = date;
            rsb["orderRequest.from_station_telecode"] = fromCode;
            rsb["orderRequest.to_station_telecode"] = toCode;
            rsb["orderRequest.train_no"] = "";
            rsb["trainPassType"] = "QB";
            rsb["trainClass"] = "QB%23D%23Z%23T%23K%23QT%23";
            rsb["includeStudent"] = "00";
            rsb["seatTypeAndNum"] = "";
            rsb["orderRequest.start_time_str"] = "00%3A00--24%3A00";


            string url = URL_QUERY_LEFT_TICKET + "&" + rsb.BuildString();
            string response = web.Get(url);

            if (response.Length < 10)
            {
                //PlaySound(@"C:\Windows\Media\Windows Critical Stop.wav", IntPtr.Zero, 1);
                Log.Debug("not ticket info.");
                return trainInfoList;
            }
            var rawArray = Regex.Matches(response, "<span.*?</a>", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            Log.Debug("total trains count {0}.", rawArray.Count);

            foreach (Match rawDataMatch in rawArray)
            {
                string rawData = rawDataMatch.Value;
                var content = Regex.Match(rawData, @"(?<=onclick=.*?)'.*?'", RegexOptions.IgnoreCase | RegexOptions.Singleline);
                var hasTicketContent = Regex.Match(rawData, @"(?<=\d{2}:\d{2},\d{2}:\d{2},).*(?=<a)", RegexOptions.IgnoreCase).Value;
                var trainName = Regex.Match(rawData, "(?<=<span.*>).*(?=</span>)", RegexOptions.IgnoreCase | RegexOptions.Singleline).Value;
                var errorInfo = Regex.Match(rawData, "(?<=<a.*>).*(?=</a>)", RegexOptions.IgnoreCase | RegexOptions.Singleline).Value;

                OrderData odata = new OrderData();
                trainInfoList.Add(odata);



                #region fill order form

                try
                {
                    odata.train_date = date;
                    odata.from_station_telecode_name = fromTelName;
                    odata.to_station_telecode_name = toTelName;
                    odata.round_train_date = date;
                    odata.round_start_time_str = "00:00--24:00";
                    odata.single_round_type = "1";
                    odata.train_pass_type = "QB";
                    odata.train_class_arr = "QB#D#Z#T#K#QT#";
                    odata.start_time_str = "00:00--24:00";
                    odata.include_student = "00";

                    odata.from_station_telecode = fromCode;
                    odata.to_station_telecode = toCode;


                    string[] arr = content.Value.Trim('\'').Split('#');
                    odata.station_train_code = trainName;

                    if (!content.Success)
                    {
                        if (!errorInfo.Contains("nbsp"))
                            odata.Status = errorInfo;
                        odata.station_train_code = trainName;
                        continue;
                    }

                    odata.station_train_code = arr[0];                                  //G101

                    odata.seattype_num = "";
                    odata.from_station_telecode = arr[4];                                   //VNP
                    odata.to_station_telecode = arr[5];                                  //AOH
                    odata.lishi = arr[1];                                              //05:23
                    odata.train_start_time = arr[2];                                   //07:00
                    odata.trainno4 = arr[3];                                   //240000G10102
                    odata.arrive_time = arr[6];                                   //12:23
                    odata.from_station_name = arr[7];                                   //北京南
                    odata.to_station_name = arr[8];                                   //上海虹桥
                    odata.from_station_no = arr[9];                                   //01
                    odata.to_station_no = arr[10];                                   //08
                    odata.ypInfoDetail = arr[11];                                   //O*****0475M*****01169*****0026
                    odata.mmStr = arr[12];                                   //252A281C7DCDAE2ED53E26A8B22FC748398D69AE4C0E7A07E79C2896
                    odata.locationCode = arr[13];                                   //P3
                }
                catch (IndexOutOfRangeException)
                {
                    Log.Error("IndexOutOfRangeException");
                }
                #endregion


                #region fill left ticket form

                bool hasValidTicket = false;

                #region  test code
                StringBuilder leftTicketTextInfo = new StringBuilder(DateTime.Now.ToString("hh:mm:ss"));
                //string order copied from webpage
                string[] nameSplitted = "商务座,特等座,一等座,二等座,高级软卧,软卧,硬卧,软座,硬座,无座,其他".Split(',');
                string[] valueSplitted = new string[nameSplitted.Length];
                try
                {
                    int idx = 0;

                    foreach (string s in hasTicketContent.Split(','))
                    {
                        var matchFull = Regex.Match(s, @"((?<=<font.*)有|(?<=<font.*>)\d+|\d+(?=$))", RegexOptions.IgnoreCase);
                        valueSplitted[idx] = matchFull.Value;

                        if (matchFull.Success)
                        {
                            leftTicketTextInfo.AppendFormat("[{0},{1}] ", nameSplitted[idx], valueSplitted[idx]);
                        }

                        if (nameSplitted[idx] != "无座" && nameSplitted[idx] != "其他")
                            hasValidTicket = hasValidTicket == true ? true : matchFull.Success;

                        idx++;

                        if (idx == nameSplitted.Length)
                            break;
                    }
                }
                catch (Exception)
                {
                }
                #endregion

                Match match = Regex.Match(hasTicketContent, @"(?<=<font.*)有|(?<=<font.*>)\d+|\d+(?=,)", RegexOptions.IgnoreCase);
                //<font color='#008800'>有</font>


                bool hasTicket = match.Success && hasValidTicket;

                if (hasTicket)
                {
                    odata.Status = leftTicketTextInfo.ToString();
                    odata.HasTicket = true;
                }
                else
                {
                    odata.Status = "NA";
                }


                #endregion
            }
            trainInfoList.Sort();
            return trainInfoList;

        }

        /// <summary>
        /// submit order. if return not-null value, represents this is fatal error should stop, otherwise can retry again.
        /// </summary>
        /// <param name="data"></param>
        /// <param name="user"></param>
        /// <param name="inputCaptchaThread"></param>
        /// <returns>return message as result. empty means action failed.</returns>
        public string SubmitOrderRequest(OrderData data, List<UserData> user, Func<Image, string> inputCaptchaThread)
        {
            System.Net.HttpStatusCode code = 0;
            Dictionary<string, string> headers = new Dictionary<string, string>()
                {
                    {"Accept-Encoding","gzip, deflate"},
                    {"Accept-Language","zh-CN"}
                };


            string postData = data.ToPostString();
            string response = string.Empty;
            CheckThreadStateToAbort();

            response = web.Post(URL_POST_SUBMIT_ORDER, postData, null, headers, out code);

            this.ReadTicketInfo(response);
            Log.Message("已经查询到 {0} 余票状态。", data.station_train_code);

            //the value of 'lishi' is assigned in VerifyHasTicket()
            //when '有票' the field will have a value represent the seat code
            JsonObject ticket = null;
            string popup = this.GetPopupMessage(response);
            if (!string.IsNullOrEmpty(popup))
            {
                Log.Error(popup);
                return popup;
            }

            if (!this.VerifyHasTicket(response) ||
                (ticket = Global.SeatTypeTargets.Find(j => !string.IsNullOrEmpty(j.lishi))) == null)
            {
                Log.Error("提交失败，选中类型无票。");
                return "选中类型无票。";
            }


            var submitDataDict = this.ReadOrderRequestFormData(response);

            StringBuilder submitText = new StringBuilder();

            StringBuilder userInfoString = new StringBuilder();
            string[] infoArr = new string[user.Count];
            for (int idx = 0; idx < user.Count; idx++)
            {
                var userInfo = user[idx];
                if (string.IsNullOrEmpty(userInfo.passenger_1_name) || string.IsNullOrEmpty(userInfo.passenger_1_cardno))
                {
                    infoArr[idx] = userInfo.BuildEmptyString();
                }
                else
                {
                    userInfo.passenger_1_seat = ticket.id;
                    infoArr[idx] = userInfo.BuildFormRequestString(submitDataDict);
                }
            }
            userInfoString.Append(string.Join("&", infoArr));


            foreach (var pair in submitDataDict)
            {
                submitText.AppendFormat("&{0}={1}", pair.Key, System.Web.HttpUtility.UrlEncode(pair.Value));
            }


            string orderResponse = string.Empty;


            string ccc = string.Empty;
            string orderPost = string.Empty;
            string orderId = string.Empty;
            Func<bool> PostPreCheck =
                () =>
                {
                    CheckThreadStateToAbort();
                    var image = web.DownloadCaptcha(URL_SUBMIT_CAPTCHA);
                    if (image == null)
                    {
                        throw new InvalidOperationException("下载验证码发生错误。");
                    }

                    ccc = inputCaptchaThread(image);

                    orderPost = string.Format("{0}&{1}&randCode={2}&orderRequest.reserve_flag=A&tFlag=dc", submitText.ToString().Trim('&'), userInfoString.ToString().Trim('&'), ccc);
                    orderResponse = web.Post(URL_POST_ORDER_CHECK + ccc, orderPost, null, headers, out code);
                    string msg = ReadJson(orderResponse)["errMsg"];

                    if (string.Compare(msg, "Y", true) != 0)
                    {
                        Log.Error("错误 {0}. ", msg);
                        return false;
                    }
                    return true;
                };


            Func<bool> FinalPost =
                () =>
                {
                    CheckThreadStateToAbort();
                    orderPost = string.Format("{0}&{1}&randCode={2}&orderRequest.reserve_flag=A", submitText.ToString().Trim('&'), userInfoString.ToString().Trim('&'), ccc);
                    orderResponse = web.Post(URL_POST_COMFIRM_TICKET_ORDER, orderPost, null, headers, out code);
                    var dict = ReadJson(orderResponse);
                    string msg = dict["errMsg"];

                    if (string.Compare(msg, "Y", true) != 0)
                    {
                        string errCode = string.Empty;
                        if (dict.ContainsKey("errCode"))
                        {
                            errCode = dict["errCode"];
                        }

                        Log.Error("错误 {0} {1}", errCode, msg);
                        return false;
                    }
                    return true;
                };


            Func<bool> WaitOrder =
                () =>
                {
                    CheckThreadStateToAbort();
                    var orderRaw = ReadJson(web.Get(URL_OrderWaitTime));
                    if (orderRaw.ContainsKey("orderId"))
                    {
                        orderId = orderRaw["orderId"];
                    }
                    return !string.IsNullOrEmpty(orderId);
                };



            for (int i = 0; i < 5; i++)
            {
                if (PostPreCheck())
                    break;
            }

            FinalPost();

            for (int i = 0; i < 5; i++)
            {
                if (WaitOrder())
                {
                    Log.Message("order id {0}", orderId);
                    break;
                }
            }

            return orderId;
        }

        public void Abort()
        {
            web.Abort();
        }

        private string IsAlreadyLogin(string source)
        {
            Match match = Regex.Match(source, @"(?<=<h1 .*>).*您好！(?=</h1>)");
            return match.Value;
        }

        private string GetPopupMessage(string htmltext)
        {
            Match err = Regex.Match(htmltext, @"(?<=<script language=""javascript"">.*var message = "").*(?="";.*var messageShow)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            return err.Value;
        }

        private bool VerifyHasTicket(string text)
        {
            var matches = Regex.Matches(text, @"(?<=<td>).*?(.*元).*(有票|\d+张票)(?=</td>)", RegexOptions.IgnoreCase);
            Global.SeatTypeTargets.TrueForAll(s => { s.lishi = null; return false; });
            foreach (Match m in matches)
            {
                if (!m.Success)
                    continue;
                //display all availble tickets and update info at background
                Log.Message(m.Value);

                foreach (var obj in Global.SeatTypeTargets)
                {
                    if (m.Value.Contains(obj.value))
                    {
                        //this value represents the seat code for submit
                        obj.lishi = m.Value;
                        break;
                    }
                }
            }

            return matches.Count > 0;
        }

        private void ReadTicketInfo(string text)
        {
            List<JsonObject> allObjects = new List<JsonObject>();
            var matchCollection = Regex.Matches(text, @"var .*?(\[\{|\{)?.*?(\]\}|\})?;", RegexOptions.IgnoreCase | RegexOptions.Multiline);

            foreach (Match tmp in matchCollection)
            {
                var singleObjectText = Regex.Matches(tmp.Value, @"\[\{.*?\}\]", RegexOptions.IgnoreCase);

                foreach (Match objectMatch in singleObjectText)
                {
                    using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(objectMatch.Value)))
                    {
                        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<JsonObject>));
                        List<JsonObject> list = (List<JsonObject>)serializer.ReadObject(ms);

                        allObjects.AddRange(list);
                    }//deserializer objects
                } // end of handling json objects in parsed lines
            } //end of handling all json in response


            foreach (var jobj in allObjects)
            {
                bool containsKey = false;
                foreach (var savedObj in Global.TicketCodeList)
                {
                    if (jobj.id == savedObj.id && jobj.value == savedObj.value)
                    {
                        containsKey = true;
                        break;
                    }
                }

                if (!containsKey && !string.IsNullOrEmpty(jobj.id))
                {
                    Global.TicketCodeList.Add(jobj);
                }
            }

        }

        private Dictionary<string, string> ReadOrderRequestFormData(string text)
        {
            var matches = Regex.Matches(text, @"<input type=['""]{1}hidden['""]{1}.*?>", RegexOptions.IgnoreCase | RegexOptions.Singleline);

            Dictionary<string, string> inputDict = new Dictionary<string, string>();

            List<string> ignored = new List<string>()
            {
                //"textfield","checkbox0","checkbox9", "orderRequest.reserve_flag", 
                //"passenger_2_seat_detail","passenger_3_seat_detail","passenger_4_seat_detail","passenger_5_seat_detail",
            };

            foreach (Match m in matches)
            {
                string name = Regex.Match(m.Value, @"(?<=name="").*?(?="")", RegexOptions.IgnoreCase).Value;
                string val = Regex.Match(m.Value, @"(?<=value="").*?(?="")", RegexOptions.IgnoreCase).Value;


                if (string.IsNullOrEmpty(name) || inputDict.ContainsKey(name))
                {
                    continue;
                }

                if (!ignored.Contains(name))
                    inputDict.Add(name, val);
            }

            return inputDict;
        }

        public Dictionary<string, string> ReadJson(string raw)
        {
            try
            {
                JavaScriptSerializer j = new JavaScriptSerializer();
                if (raw.Contains("["))
                {
                    List<Dictionary<string, string>> list = j.Deserialize<List<Dictionary<string, string>>>(raw);
                    return list[0];
                }
                else
                {
                    Dictionary<string, string> d = j.Deserialize<Dictionary<string, string>>(raw);
                    return d;
                }
            }
            catch (Exception)
            {
                return new Dictionary<string, string>();
            }
        }

        public static void CheckThreadStateToAbort()
        {
            bool reasonFormReCreated = MainForm.Instance.Visible &&
                (Thread.CurrentThread.ThreadState == ThreadState.Running || Thread.CurrentThread.ThreadState == ThreadState.Background);

            if (reasonFormReCreated)
            {
                Debug.WriteLine(
                    string.Format("Thread {0} was aborted by reasonFormReCreated {1}",
                    Thread.CurrentThread.ManagedThreadId,
                    reasonFormReCreated));

                if (CancelState)
                    throw new InvalidOperationException("cancelled.");
            }
        }


        public const string URL_HOME = "https://dynamic.12306.cn/otsweb/main.jsp";
        private const string URL_HOME_MY = "https://dynamic.12306.cn/otsweb/loginAction.do?method=initForMy12306";


        //all webpages use this to generate verification code 


        private const string URL_lOGIN_CAPTCHA = "https://dynamic.12306.cn/otsweb/passCodeAction.do?rand=sjrand";
        private const string URL_SUBMIT_CAPTCHA = "https://dynamic.12306.cn/otsweb/passCodeAction.do?rand=randp";

        //login urls
        const string URL_LOGIN_PAGE = @"https://dynamic.12306.cn/otsweb/loginAction.do?method=init";
        private const string URL_POST_LOGIN_PRE = "https://dynamic.12306.cn/otsweb/loginAction.do?method=loginAysnSuggest";
        const string URL_POST_LOGIN = @"https://dynamic.12306.cn/otsweb/loginAction.do?method=login";

        //train id, seat type info

        const string URL_HOME_SEATTYPES = @"https://dynamic.12306.cn/otsweb/order/querySingleAction.do?method=init";
        const string URL_STATION_NAMES_JS = @"https://dynamic.12306.cn/otsweb/js/common/station_name.js?version=1.3";

        //from locationA to locationB, train info
        const string URL_POST_QUERY_AVAILABLE_TRAINS = @"https://dynamic.12306.cn/otsweb/order/querySingleAction.do?method=queryststrainall";
        const string URL_QUERY_LEFT_TICKET = "https://dynamic.12306.cn/otsweb/order/querySingleAction.do?method=queryLeftTicket";

        //TODO: URL TYPO
        //order request urls
        const string URL_POST_SUBMIT_ORDER = "https://dynamic.12306.cn/otsweb/order/querySingleAction.do?method=submutOrderRequest";
        const string URL_POST_COMFIRM_TICKET_ORDER = "https://dynamic.12306.cn/otsweb/order/confirmPassengerAction.do?method=confirmSingleForQueueOrder";
        const string URL_POST_ORDER_CHECK = @"https://dynamic.12306.cn/otsweb/order/confirmPassengerAction.do?method=checkOrderInfo&rand=";
        const string URL_OrderWaitTime = "https://dynamic.12306.cn/otsweb/order/myOrderAction.do?method=getOrderWaitTime&tourFlag=dc";


        const string URL_CONFIRM_ACTION = "https://dynamic.12306.cn/otsweb/order/confirmPassengerAction.do?method=init";
    }


    static class Helper
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="raw">copied from fiddler form</param>
        /// <returns></returns>
        public static string GenerateOdata(string raw)
        {
            string[] queryLeftRaw =
                "G101#05:23#07:00#240000G10102#VNP#AOH#12:23#北京南#上海虹桥#01#08#O*****0475M*****01169*****0026#252A281C7DCDAE2ED53E26A8B22FC748398D69AE4C0E7A07E79C2896#P3".Split('#');

            StringBuilder sb = new StringBuilder();
            using (StringReader sr = new StringReader(raw))
            {
                while (sr.Peek() > 0)
                {
                    string line = sr.ReadLine();
                    string[] splitted = line.Split('\t');

                    if (splitted.Length > 0)
                    {
                        string key = splitted[0].Trim();
                        string value = line.Substring(line.IndexOf('\t')).Trim();
                        int idx = 0;
                        for (; idx < queryLeftRaw.Length; idx++)
                        {
                            if (string.Compare(queryLeftRaw[idx], value, true) == 0)
                                break;
                        }


                        OrderData odata = new OrderData();
                        foreach (var item in odata.GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public))
                        {
                            if (string.Compare(item.Name, key, true) == 0)
                            {
                                if (idx != queryLeftRaw.Length)
                                {
                                    sb.AppendLine(string.Format("odata.{0} = arr[{1}];   {2, -30}  //{3}", item.Name, idx, " ", value));
                                }
                                else
                                {
                                    sb.AppendLine(string.Format(" odata.{0} = \"{1}\"; ", item.Name, value));
                                }



                            }
                        }
                    }

                }

                return sb.ToString();
            }

        }


    }
}
